"""
jackson.rttopt.net or jackson.rttopt.net.net_requests

rttopt protocol messages 
"""

from jackson import debug

debug.pre_import(__name__)

from django.conf import settings
import urllib, urllib2

from jackson.utils import gen_hash

import jackson.extjson.ext_json as json

from jackson.chord.db import get_me
from jackson.chord import db
# note: this line fails if not imported this way
import jackson.rttopt as rttopt
from jackson.chord.db import i_am_active
import chord.client_api as client_api 

from jackson.rttopt.utils import CIDRstr

from random import choice

debug.post_import(__name__)

def leave(leaving_server,timestamp):
    """ send a message to all servers in finger table that a node is leaving
    """
    context = [__name__, "leave()"]
    debug.enter(context, {'leaving server': leaving_server.ip +":" +  str(leaving_server.port)})
    responses = [] # store the response from each finger
    me = get_me()
    for i in range(1, settings.M + 1):
        next_finger = db.get_finger(i)
        if next_finger.ip == me.ip and next_finger.port == me.port:
            debug.comment(context,"neglecting to send a leave message to ourselves. next_finger...")
            continue
        json_data = json.dumps({'server': leaving_server, 'timestamp' : timestamp})
        f = urllib2.urlopen('http://' + str(next_finger.ip) + ':' + str(next_finger.port) + 
                            '/rttopt/leave', urllib.urlencode({'data': json_data}))
        if not f.info().gettype() == 'application/json':
            debug.comment(context,"server " + str(next_finger) + " is a fuckup.")
            continue
        r = json.loads(f.read())
        responses.append(r)
        debug.comment(context, "server accepting the leave says: " + str(r))
    if len(responses) > 4:
        r = responses[:5]
    else:
        r = None
    debug.leave(context,{'a few resposes':r})

def send_rtt_update(rtt, ip):
    """
    Given an RTT and the IP that corresponds to that RTT, sends updates to the
    appropriate nodes that store the corresponding IP tables.
    
    @change: 3-18-09 this now will try to process each mask (8,16,24) and 
    return a list of responses
    
    @param rtt: a round-trip time recorded by rttopt
    @param ip: the IP to which the RTT was recorded
    """
    context = [__name__, "send_rtt_update()"]
    debug.enter(context, {'rtt': rtt, 'ip': ip})
    if not i_am_active():
        debug.leave(context, {"failure reason":"chord no longer active"})
        return None
    responses = [] # store the response for each IP block
    me = get_me()
    for mask in range(8, 32, 8):
        cstr = CIDRstr(ip, mask)
        key = gen_hash(cstr)
        owner = client_api.lookup(key)
        json_data = json.dumps({'rtt': rtt, 'key': key, 'server': me, 'range': cstr})
        f = urllib2.urlopen('http://' + str(owner.ip) + ':' + str(owner.port) + '/rttopt/update',
                            urllib.urlencode({'data': json_data}))
        assert f.info().gettype() == 'application/json'
        r = json.loads(f.read())
        responses.append(r)
        debug.comment(context, "server accepting the rtt update says: " + str(r))
    debug.leave(context, None)
    return responses


def lookup(ip):
    """
    Queries the successors of the given IP's CIDRizations to see which server
    has the fastest RTT to it.
    @param ip: an IPv4 IP
    @rtype a Server object representing the fastest server, or False if there is none
    """    
    context = [__name__, "lookup()"]
    me = get_me()
    debug.enter(context, {'ip': ip})
    server_rtt_tuple_list = []
    for mask in range(8, 32, 8):
        cstr = CIDRstr(ip, mask)
        key = gen_hash(cstr)
        json_data = json.dumps({'key': key})
        debug.comment(context,"asking chord who's responsible for " + cstr + " (" + key + ")")
        owner = client_api.lookup(key)
        debug.comment(context,"calling rttopt/lookup on " + str(owner))
        f = urllib2.urlopen('http://' + str(owner.ip) + ':' + str(owner.port) + '/rttopt/lookup',
                            urllib.urlencode({'data': json_data}))
        try:
            assert f.info().gettype() == 'application/json'
            d = json.loads(f.read())
            try:
                server_rtt_pair = (d['server'],d['rtt'])
                server_rtt_tuple_list.append(server_rtt_pair)
            except Exception, e:
                debug.exception(context,"failure in net_requests.lookup(): " + str(e))
        except Exception, e:
            debug.exception(context,"wrong mimetype '" + f.info().gettype() + "' returned from lookup request")
    # grab the lowest RTT from the ones we are given
    min_rtt = min([rtt for (server,rtt) in server_rtt_tuple_list])
    # pick one at random that has the min value
    min_rtt_servers = [pair for pair in 
                       server_rtt_tuple_list if rtt == min_rtt]
    server_rtt_pair = choice(min_rtt_servers)
    s = server_rtt_pair[0]
    debug.leave(context, {'server':s})
    return s

